/*-------------------------------------------------------------------------
   monitor.c - simple monitor

   Copyright (C) 2010 One Laptop per Child

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   In other words, you are welcome to use, share and improve this program.
   You are forbidden to forbid anyone else to use, share and improve
   what you give them.   Help stamp out software-hoarding!
-------------------------------------------------------------------------*/

#include <ctype.h>
#include <stdio.h>
#include "chip.h"
#include "adc.h"
#include "uart.h"
#include "sdi.h"
#include "power.h"
#ifndef NO_PS2
#include "ps2.h"
#include "ps2_private.h"
#endif
#include "touchpad.h"
#include "keyboard.h"
#include "onewirebatt.h"
#include "timer.h"
#include "io.h"
#include "battery.h"
#include "mppt.h"
#include "stop.h"
#include "build.h"
#include "ols.h"
#include "charger.h"
#include "event.h"
#include "ac.h"
#include "every.h"

#define ctrl(c) (c ^ 0x40)
#define DEL 0x7f

#define DEBUG_CMDS 0

static unsigned char data line[16];
static unsigned char data l;
static unsigned int xdata addr;
static bool addr_is_data;

static bool getline(void)
{
	char c;

	if (!char_avail())
		return 0;

	c = getchar();

#ifdef FANCY_PARSE
	// eat leading spaces
	if (l == 0 && c == ' ')
		return 0;
#endif

#ifdef FANCY_PARSE
	// special for the "+ - = < >" commands
	if (l == 0 && (c == '+' || c == '-' || c == '='
				   || c == '<' || c == '>') )
	{
		line[l++] = c;
		line[l] = '\0';
		putchar('\r'); // retreat over the prompt
		return 1;
	}
#endif

	if (c == '\r' || c == '\n')
	{
		// done
		putchar('\n');
		putchar('\r');
		return 1;
	}

	putchar(c);

	// backspace
	if (c == '\b' || c == DEL)
	{
		putchar(' ');
		putchar('\b');
		if (l > 0) l--;
		line[l] = '\0';
		return 0;
	}

	// accumulate the character
	if (l < (sizeof(line) - 2))
	{
		line[l++] = c;
		line[l] = '\0';
		return 0;
	}

#ifdef FANCY_PARSE
	// line too long
	if (isprint(c))
	{
		putchar('\b');
		putchar(' ');
		putchar('\b');
	}
#endif
	return 0;
}

static char tohex(char c)
{
	/* only works if isxdigit(c) is true */
	if (c >= 'a')
		return c - 'a' + 10;
	else if (c >= 'A')
		return c - 'A' + 10;
	else
		return c - '0';
}

static bool gothex;
static unsigned int gethex(void)
{
	unsigned int n = 0;
	gothex = 0;
	while (line[l] == ' ')
	{
		l++;
	}
	while (isxdigit(line[l]))
	{
		gothex = 1;
		n = (n << 4) | tohex(line[l]);
		l++;
	}
	return n;
}

static void prompt(void)
{
	l = 0;
	line[0] = '\0';
	putchar('>');
}

void monitor(void)
{
	unsigned int n;
	unsigned char cmd, m;

	if (!getline()) return;

	l = 0;
	cmd = line[l++];
	n = gethex();
	switch (cmd)
	{
		case '\0':
			break;

		case '1':
			onewire_debug_dump();
			break;

		case '2':
			onewire_enable_show_states();
			break;

#if 0
		case 'a': // adc
			printf("adc chan %d is 0x%x\n", n, read_ad_ch(n));
			break;

		case 'A': // adc_init
			if (n)
				adc_init();
			else
				adc_deinit();
			break;
#endif

		case 'b': // battery
			battery_set_debug(n);
			break;

		case 'B': // USB power
			power_usb(n);
			break;

#if DEBUG_CMDS
		case 'c':
			DAC0 = n;
			printf("DAC0 %x\n",(unsigned int)DAC0 );
			break;

		case 'C':
			printf("Chg Cur=%d\n",ISL_get_charge_current());
			break;
#endif
			/* 'd' used below */
#if 0
		case 'D':  // calibrate the delay timer
		{
			long then, now;
			then = get_ms_timer_ext();
			short_delay(10000);
			short_delay(10000);
			short_delay(10000);
			short_delay(10000);
			short_delay(10000);
			short_delay(10000);
			short_delay(10000);
			short_delay(10000);
			short_delay(10000);
			short_delay(10000);
			now = get_ms_timer_ext();
			printf("\n%ld ms/100000 loops\n", now - then);
		}
#endif
		break;

#if DEBUG_CMDS
		case 'e':
			battery_cmd_runin_discharge_enable();
			break;
		case 'E':
			battery_cmd_runin_discharge_disable();
			break;

		case 'F':
			printf("Inp Cur=%d\n",ISL_get_last_input_current());
			break;
		case 'i':
			ac_adjust_input_power();
			break;
#endif

#if 0
		case 'G':
			if (n)
			{
				set_LED_CHG_GREEN_on();
			}
			else
			{
				set_LED_CHG_GREEN_off();
			}
			break;
#endif
#ifndef NO_PS2
		case 'j': // turn on mouse
			touchpad_stream_on();

		case 'K':  // keyboard and touchpad debug control
			key_set_debug(n & 1);
			touchpad_set_debug(n & 2);
			break;
		case 'k':
			ps2_kick_tx();
			break;
#endif
		case 'k':
			event_set_event_mask(n);
			break;

		case 'l':
			printf("%d\n",ols_get_reading());
			break;

		case 'L':
			ols_set_debug(n);
			break;

		case 'M':
			mppt_cmd_enable(n);
			break;
#if DEBUG_CMDS
		case 'm':
			if (gothex)
				mppt_cmd_set_limit(n);
			else
				printf("power_limit is 0x%x\n", (unsigned int)mppt_get_limit());
			break;

		case 'n':
			if (gothex)
				mppt_set_minpower_limit(n);
			else
				printf("Min limit is 0x%x\n", (unsigned int)mppt_get_minpower_limit());
			break;

		case 'N':
			if (gothex)
				mppt_set_start_power_limit(n);
			else
				printf("Limit start is 0x%x\n", (unsigned int) mppt_get_start_power_limit());
			break;
#endif

#if 0
		case 'O':
			if (n & 1)
				input_enable_CHRG_DQ();
			else
				input_disable_CHRG_DQ();
			if (n & 2)
				output_enable_CHRG_DQ();
			else
				output_disable_CHRG_DQ();
			// fallthrough
#endif
#if DEBUG_CMDS
		case 'o':
			printf("DQ: %x\n",(unsigned int) read_DQ() );
			break;
#endif
		case 'P':
			if (n)
				power_start_host();
			else
				power_command_shutdown();
			break;

#if 0
		case 'q':
			enable_EC_IRQ();
			short_delay(n);
			disable_EC_IRQ();
			break;
#endif

#ifndef NO_PS2
		case 'p':
			ps2_dump();
			if (n) ps2_clk_ctrl(n);
			break;
#endif

#if 0
		case 'r':
			E51_RST = 1;
			/* probably not reached */
			while (1) /* spin */;
			break;
#endif

#if 0
		case 'R':
			if (n)
			{
				set_LED_CHG_RED_on();
			}
			else
			{
				set_LED_CHG_RED_off();
			}
			break;
#endif

		case 's':
			break;

		case 'S':
			sdi_set_debug(n);
			break;

		case 'T':
			enable_EN_KBD_PWR();
			break;
		case 't':
			disable_EN_KBD_PWR();
			break;

#ifdef EVENT_DEBUG
		case 'u':
			event_send_event(n);
			break;

		case 'U':
			event_set_repeat(n, gethex());
			break;
#endif

#if 0
		case 'U':
			uart_calibrate();
			break;
#endif

		case 'v':
			build_banner();
			break;

		case 'W':  /* force a watchdog reset */
			switch (n)
			{
				case 0:  // no arg given -- force a reboot
					watchdog_reboot();

					// otherwise, exercise watchdog_start()/stop() (which only
					// work in stop mode.)
				case 1:  // debug support
					watchdog_stop();
					break;

				default:
					watchdog_start(n);

			}
			break;

		case 'w':  // write 1 byte to data or xdata
			addr = n;
			m = gethex();
			if (addr < 0x100)
				*(unsigned char data *)addr = m;
			else
				*(unsigned char xdata *)addr = m;
			break;

		case 'x':  //  read 1 byte from xdata
		case 'd':  //  read 1 byte from data
			addr = n;
			addr_is_data = (cmd == 'd');
			// fallthrough

#ifdef FANCY_PARSE
		case '=':
		case '+':
		case '-':
			if (cmd == '+')
				addr++;
			else if (cmd == '-')
				addr--;
#endif

			if (addr_is_data)
				addr &= 0xff;

			printf("%04x: %02x\n", (uint)addr,
				   addr_is_data ?
				   (uint)*(unsigned char data *)addr :
				   (uint)*(unsigned char xdata *)addr);
			break;

		case 'z':
		{
			extern unsigned char soc_sleep_eco_present;
			if (n > 1)
			{
				soc_sleep_eco_present = (n & 1);
				break;
			}
			enable_stop_mode(n);
			break;
		}

		case 'Z':
			enter_stop_mode(0, 0);
			printf("back from stop mode\n");
			break;

		case '<':
			mppt_change_power_limit(-1);
			break;
		case '>':
			mppt_change_power_limit(1);
			break;

#define TUNE_BAUDRATE 0
#if TUNE_BAUDRATE
		case '7':
			SCON3 -= 1;
			printf("SCON3 is 0x%x\n", (uint)SCON3);
			break;

		case '8':
			printf("SCON3 is 0x%x\n", (uint)SCON3);
			while (!char_avail())
				putchar('U');
			break;

		case '9':
			SCON3 += 1;
			printf("SCON3 is 0x%x\n", (uint)SCON3);
			break;
#endif

#if DEBUG_CMDS
		case '4':
			timed_host_wakeup_cmd_enable(n);
			break;
		case '5':
			timed_host_wakeup_cmd_autorepeat(n);
			break;
		case '6':
			timed_host_wakeup_cmd_value((unsigned long)n);
			break;
#endif
		default:
			puts("?");
	}

	prompt();
}
